﻿using System;
using System.Collections;
using System.Collections.Generic;
using System.Linq;
using System.Reflection;
using ValidationEngine.Commons;
using ValidationEngine.Commons.Enums;
using ValidationEngine.Commons.Interfaces;
using ValidationEngine.Tools;

namespace ValidationEngine.ValidationRules
{
    public class UniqueValueRule : BaseValidationRule
    {
        #region Members
        private readonly string _itemPropName;
        private readonly string _notUniqueErrorText; 
        #endregion

        protected override IValidationInfo ApplyRule(IValidationContext ctx)
        {
            IList list = null;
            ModelHelper.TryGetCollectionForProperty(ctx, out list);
            bool duplicatesPresent = false;
            if (null != list)
            {
                Type itemType = list.AsQueryable().ElementType;
                PropertyInfo itemProp = itemType.GetProperty(_itemPropName);
                if (null != itemProp)
                {
                    Dictionary<object, List<int>> values = new Dictionary<object, List<int>>();
                    for (int idx = 0; idx < list.Count; idx++)
                    {
                        List<int> indexes;
                        object obj = itemProp.GetValue(list[idx], null);
                        if (!values.TryGetValue(obj, out indexes))
                        {
                            indexes = new List<int>();
                            values.Add(obj, indexes);
                        }

                        indexes.Add(idx);
                    }

                    List<object> dublicates = values.Where(pair => pair.Value.Count > 1).Select(pair => pair.Key).ToList();
                    duplicatesPresent = dublicates.Any();
                    if (typeof(IValidateableModel).IsAssignableFrom(itemType))
                    {
                        foreach (var duplicate in dublicates)
                        {
                            List<int> indexes = values[duplicate];
                            foreach(var index in indexes)
                            {
                                var res = new ValidationInfo();
                                res.AddError(_notUniqueErrorText);
                                ModelHelper.SetModelPropertyInfo(new ValidationContext { Model = (IValidateableModel)list[index], PropertyName = _itemPropName }, res);
                            }
                        }
                    }
                }
            }

            DefResult = duplicatesPresent ? _defErrorMsg : null;
            return Result;
        }

        #region Ctor
        public UniqueValueRule(
           string propName,
           string defErrorText = "Duplicates present.",
           string notUniqueErrortext = "Not unique.",
           RuleApplingContext applyCtx = RuleApplingContext.Always,
           ValidationErrorType errorType = ValidationErrorType.Error)
        {
            this._itemPropName = propName;
            this._defErrorMsg = defErrorText;
            this.ApplingContext = applyCtx;
            this._defErrorType = errorType;
            this._notUniqueErrorText = notUniqueErrortext;
        } 
        #endregion
    }
}
